[Docker] JDKのイメージを新しくしたらSpring Bootアプリケーションが起動しなくなった話
先日、Debianの古いバージョンが軒並み削除されたのは記憶に新しいところです。 この影響で、古いDebianのイメージから作られている各種Dockerイメージがビルドに失敗するようになりました。
2019年3月25日 Debian,Wheezyなど古いバージョンをミラーサイトから削除へ
自分の周りでも、開発・運用しているSpring Bootアプリケーションがこの影響を受け、イメージのビルドができなくなりました。そのため、依存しているopenjdkのイメージを変更したところ、イメージのビルドはできるようになったものの、なぜかアプリケーションが起動しなくなりました。その原因について、後学のため記事として残しておきます。
事象
開発・運用中のSpring BootアプリケーションのDockerイメージをビルドしてみたところ、なぜかビルドに失敗するようになりました。調査したところ、以下のようなことが判明しました。
- Dockerfileは
FROM java:openjdk-8-jdk
となっていました。これはdebian:jessie
を元にしているため、Debianの古いバージョンが削除された影響を受けていました。 - そこでopenjdkのイメージを変更し
FROM openjdk:8-jdk-stretch
としました。 - すると、イメージのビルドは成功するようになったのですが、なぜか起動できなくなりました。
原因その1
調べると、環境変数で spring.profiles.active=foo,bar
を指定しているはずが、なぜか反映されていないことがわかりました。
Spring Bootでは、環境変数からプロパティを指定する際は SPRING_PROFILES_ACTIVE=foo,bar
の形式で指定することができます(というかこっちの方が正しい)。そこで設定を書き換えたところ、起動するようになりました。
これで一応起動するようにはなったのですが、なぜ環境変数が反映されなくなったのかが分からなかったため、さらに調べてみました。
原因その2
色々調べたところ、下記のissueが見つかりました。これが原因でした。
https://github.com/docker-library/openjdk/issues/135
詳しくは、以下の2点が原因です。
一部の環境で /bin/sh
がドットを含む環境変数を無視する
下記の環境では、/bin/sh
ではドットを含む環境変数が使えなくなっていました。 *1
- Alpine Linuxでは3.6から
- https://github.com/docker-library/openjdk/issues/135#issuecomment-318191456
- https://bugs.alpinelinux.org/issues/7344
- Debianではdash 0.5.8-2.4から (
/bin/sh
はdashへのシンボリックリンクになっている) - https://github.com/docker-library/openjdk/issues/135#issuecomment-318495067
- https://git.kernel.org/pub/scm/utils/dash/dash.git/commit/?h=v0.5.8&id=46d3c1a614f11f0d40a7e73376359618ff07abcd
今回使用した openjdk:8-jdk-stretch
は debian:stretch
を元にしているため、上記の影響を受け /bin/sh
ではドットを含む環境変数が使用できなくなっています。
実際に試してみると、ドットを含む環境変数 jp.classmethod=test
が、/bin/sh
からは見えなくなっています。
# 単にenvとすると普通に出る $ docker run -it --rm -e jp.classmethod=test openjdk:8-jdk-stretch env | grep jp.classmethod jp.classmethod=test # /bin/shを明示すると出ない $ docker run -it --rm -e jp.classmethod=test openjdk:8-jdk-stretch /bin/sh -c "env" | grep jp.classmethod
DockerのCMDはシェル形式だと /bin/sh を使用する
ではどこで /bin/sh
が出てくるのかというと、DockerfileのCMD
の仕様です。
CMD
でのコマンドの指定形式は複数ありますが、そのうち「シェル形式」で指定した場合は /bin/sh
が使われます。
http://docs.docker.jp/engine/reference/builder.html#cmd
CMD には3つの形式があります。
- CMD ["実行バイナリ", "パラメータ1", "パラメータ2"] ( exec 形式、推奨する形式)
- CMD ["パラメータ1", "パラメータ2"] ( ENTRYPOINT のデフォルト・パラメータ)
- CMD <コマンド> (シェル形式)
CMD を シェル 形式で使えば、 <コマンド> は /bin/sh -c で実行されます。
当方のSpring Bootアプリケーションでは、DockerfileのCMD
は以下のような形になっていました。これはシェル形式なので /bin/sh
から実行されることになり、前述のとおりドットを含む環境変数は無視されます。
CMD java -Dfoo.bar=baz ... -jar hoge.jar
まとめ
JDKのイメージを新しくしたらSpring Bootアプリケーションが起動しなくなったため、原因を調査しました。結果、以下の2つが重なって生じた現象でした。
- 新しい環境では
/bin/sh
からはドットを含む環境変数が使えないことがある - Dockerfileの
CMD
でシェル形式で指定したコマンドは/bin/sh
で実行される
このような現象にハマらないようにするには、以下のような対策が考えられます。
- ドットを含む環境変数を使わない(
SPRING_PROFILES_ACTIVE
のような形式を使う) - どうしても無理な場合は、
CMD
をexec形式(CMD ["foo", "bar"]
)で書く
以上です。同じことでハマった方がいれば、参考になれば幸いです。
脚注
- POSIXに準拠していないというのが理由のようです。つまりバグではなく仕様で、そもそもドットを含む変数名は使うべきではないということのようです。 ↩